<!DOCTYPE html>
<html lang="en">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="李修倩在 Github 上的个人博客">
    <meta name="keyword" content="null">
    <meta name="theme-color" content="#600090">
    <meta name="msapplication-navbutton-color" content="#600090">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="#600090">
    <link rel="shortcut icon" href="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-person-128.png">
    <link rel="alternate" type="application/atom+xml" title="Xiuqianli&#39;s blog" href="/atom.xml">
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.css">
    <title>
        
        手写山寨spring基础功能 - AOP｜xiuqianli&#39;s blog
        
    </title>

    <link rel="canonical" href="https://981764793.gitee.io/2018/07/17/手写山寨spring基础功能-AOP/">

    <!-- Bootstrap Core CSS -->
    <link rel="stylesheet" href="/css/bootstrap.min.css">

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/css/blog-style.css">

    <!-- Pygments Github CSS -->
    <link rel="stylesheet" href="/css/syntax.css">
</head>

<style>

    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }
</style>
<!-- hack iOS CSS :active style -->
<body ontouchstart="" class="animated fadeIn">
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top " id="nav-top" data-ispost = "true" data-istags="false
" data-ishome = "false" >
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header page-scroll">
            <button type="button" class="navbar-toggle">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand animated pulse" href="/">
                <span class="brand-logo">
                    Xiuqianli&#39;s blog
                </span>
                's Blog
            </a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <!-- Known Issue, found by Hux:
            <nav>'s height woule be hold on by its content.
            so, when navbar scale out, the <nav> will cover tags.
            also mask any touch event of tags, unfortunately.
        -->
        <!-- /.navbar-collapse -->
        <div id="huxblog_navbar">
            <div class="navbar-collapse">
                <ul class="nav navbar-nav navbar-right">
                    <li>
                        <a href="/">Home</a>
                    </li>
					
                    
                        
							
                        <li>
                            <a href="/Tags/">Tags</a>
                        </li>
							
						
                    
					
					
                </ul>
            </div>
        </div>
    </div>
    <!-- /.container -->
</nav>
<script>
    // Drop Bootstarp low-performance Navbar
    // Use customize navbar with high-quality material design animation
    // in high-perf jank-free CSS3 implementation
//    var $body   = document.body;
    var $toggle = document.querySelector('.navbar-toggle');
    var $navbar = document.querySelector('#huxblog_navbar');
    var $collapse = document.querySelector('.navbar-collapse');

    $toggle.addEventListener('click', handleMagic)
    function handleMagic(e){
        if ($navbar.className.indexOf('in') > 0) {
        // CLOSE
            $navbar.className = " ";
            // wait until animation end.
            setTimeout(function(){
                // prevent frequently toggle
                if($navbar.className.indexOf('in') < 0) {
                    $collapse.style.height = "0px"
                }
            },400)
        }else{
        // OPEN
            $collapse.style.height = "auto"
            $navbar.className += " in";
        }
    }
</script>

<!-- Main Content -->

<!--only post-->


<img class="wechat-title-img"
     src="http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80">


<style>
    
    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }

    
</style>

<header class="intro-header">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <div class="post-heading">
                    <h1>手写山寨spring基础功能 - AOP</h1>
                    
                    <span class="meta">
                         作者 Xiuqian Li
                        <span>
                          日期 2018-07-17
                         </span>
                    </span>
                    <div class="tags text-center">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#spring"
                           title="spring">spring</a>
                        
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="post-title-haojen">
        <span>
            手写山寨spring基础功能 - AOP
        </span>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">
            <!-- Post Container -->
            <div class="col-lg-8 col-lg-offset-1 col-sm-9 post-container">
                <h4 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h4><p>AOP跟IOC没有必然联系，但在本项目中配合着IOC进行使用，当然也可以独立使用，但是涉及到的类扫描之类的基础代码在上一节贴了，这一节就不再赘述，所以食用本文章前可先参考前一节</p>
<p><a href="https://zhuanlan.zhihu.com/p/38610359" target="_blank" rel="noopener">手写山寨spring基础功能 - IOC</a></p>
<p>在上一节中附上了IOC实现的关键代码，接下来实现spring的另一基石–<strong>AOP</strong>，<strong>Aspect Oriented Programming</strong>的缩写，意为：<strong>面向切面</strong>编程，通过预编译方式和运行期<strong>动态代理</strong>实现程序功能的统一维护的一种技术。</p>
<p>其中涉及到的最关键的设计模式就是代理模式，代理模式有两种实现方式，静态代理和动态代理，静态代理需要针对需要代理的不同的类事先写好代理类，而动态代理则不需要，由程序在运行时动态生成字节码实现代理类，基于动态代理技术可以很方便的实现AOP这一功能。</p>
<h4 id="动态代理的实现"><a href="#动态代理的实现" class="headerlink" title="动态代理的实现"></a>动态代理的实现</h4><p>我所了解到的比较常用的有两种实现方式。spring里对这两种方式都有实现，是接口的用jdk的proxy, 类就自动切换到cglib。</p>
<p>1.jdk的<strong>Proxy类</strong>，具体步骤为实现InvocationHandler接口定义自己的InvocationHandler，通过Proxy.newProxyInstance获取代理对象即可，Proxy是基于接口的，只能基于接口生成代理对象。简单的示例代码如下：<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">IHello  ihello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(),  //加载接口的类加载器</span><br><span class="line">               new Class[]&#123;IHello.class&#125;,      //一组接口</span><br><span class="line">               new HWInvocationHandler(new Hello())); //自定义的InvocationHandler</span><br><span class="line">ihello.sayHello();</span><br></pre></td></tr></table></figure></p>
<p>2.第二种方式就是本项目里采用的方式，<strong>cglib</strong>。本来一开始想学习spring的方式针对接口和类采用不同的动态代理实现方式，开发过程中发现cglib也可以基于接口生成动态代理类，所以就偷了个懒不再分别实现了，在后面的仿mybatis的jdbc模块也有用到cglib。因为cglib是可以基于类实现动态代理的，所以只要被代理的类有无参构造函数即可，实现MethodInterceptor后一句代码完事:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Enhancer.create(obj.getClass(), new AopCallback(obj))</span><br></pre></td></tr></table></figure></p>
<p>其中因为cglib依赖asm，体积较大，所以本项目依赖的是<strong>cglib-nodep</strong>。</p>
<h4 id="AOP实现的关键"><a href="#AOP实现的关键" class="headerlink" title="AOP实现的关键"></a>AOP实现的关键</h4><p>先实现MethodInterceptor，AopCallback的实现：</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class AopCallback implements MethodInterceptor &#123;</span><br><span class="line"></span><br><span class="line">    private Object target;</span><br><span class="line"></span><br><span class="line">    public AopCallback(Object target) &#123;</span><br><span class="line">        this.target = target;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public Object getTarget() &#123;</span><br><span class="line">        return target;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable &#123;</span><br><span class="line">        List&lt;AopAction&gt; actionList = getAopActionChain(method);</span><br><span class="line">    	if (CollectionKit.isEmptyCollection(actionList)) &#123;</span><br><span class="line">            return methodProxy.invoke(target, args);</span><br><span class="line">        &#125;</span><br><span class="line">        return new Invocation(target, method, args, methodProxy, actionList).invoke().getReturnVal();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    private List&lt;AopAction&gt; getAopActionChain(Method method) &#123;</span><br><span class="line">        List&lt;AopAction&gt; actionList = AopContainer.getAopActionChain(method);</span><br><span class="line">    	if (actionList != null) &#123;</span><br><span class="line">    		return actionList;</span><br><span class="line">    	&#125;</span><br><span class="line"></span><br><span class="line">        actionList = AopContainer.getClassAopActionChain(target.getClass());//获取类拦截器</span><br><span class="line">    	</span><br><span class="line">    	if (!method.isAnnotationPresent(LSAround.class)) &#123;</span><br><span class="line">    		return actionList;</span><br><span class="line">    	&#125;</span><br><span class="line">    	</span><br><span class="line">    	if (actionList == null)</span><br><span class="line">            actionList = new ArrayList&lt;&gt;();</span><br><span class="line">    	if (method.isAnnotationPresent(LSClear.class)) &#123;</span><br><span class="line">    	    //方法上有LSClear注解就清除切面</span><br><span class="line">            Class&lt;? extends AopAction&gt;[] clearClasses = method.getAnnotation(LSClear.class).value();</span><br><span class="line">            if (CollectionKit.isEmptyArray(clearClasses)) &#123;</span><br><span class="line">                actionList.clear(); //清除所有切面</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                //清除指定切面</span><br><span class="line">                actionList = actionList.stream()</span><br><span class="line">                        .filter(aopAction -&gt; !CollectionKit.inArray(clearClasses, aopAction.getClass()))</span><br><span class="line">                        .collect(Collectors.toList());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    	LSAround lsAround = method.getAnnotation(LSAround.class);</span><br><span class="line">        for (Class&lt;? extends AopAction&gt; aopActionClass : lsAround.value()) &#123;</span><br><span class="line">            AopAction aopAction = BeanContainer.getBean(aopActionClass);</span><br><span class="line">            actionList.add(aopAction);</span><br><span class="line">        &#125;</span><br><span class="line">        AopContainer.putAopActionChain(method, actionList);</span><br><span class="line">    	return actionList;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>target变量是执行方法时的真正对象，intercept是实现MethodInterceptor接口必须实现的方法，调用代理对象的方法时都会先回调这个方法，可在这里面调用真正的方法也可以调用其他方法，返回自己需要的值之类的各种操作都可以实现。</p>
<p>这个类里还有个getAopActionChain方法，用来获取方法的需要执行的所有拦截器，包括类上注解的拦截器和LSAspect注解的拦截器类，这部分之后贴代码。</p>
<p>在intercept方法里判断该方法的拦截器列表是否为空，为空就直接调用方法，否则调用Invocation执行所有拦截器。来个简单的仅基于注解的方法级的AOP实现方案</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class AopCallback implements MethodInterceptor &#123;</span><br><span class="line"></span><br><span class="line">    private Object target;</span><br><span class="line"></span><br><span class="line">    public AopCallback(Object target) &#123;</span><br><span class="line">        this.target = target;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable &#123;</span><br><span class="line"></span><br><span class="line">        if (!method.isAnnotationPresent(LSAround.class)) &#123;</span><br><span class="line">            return methodProxy.invoke(target, args);</span><br><span class="line">        &#125;</span><br><span class="line">        LSAround lsAround = method.getAnnotation(LSAround.class);</span><br><span class="line">        Class&lt;? extends AopAction&gt; aopActionClass = lsAround.value();</span><br><span class="line">        AopAction aopAction = BeanFactory.getInstance().getBean(aopActionClass);</span><br><span class="line">        return aopAction.invoke(new Invocation(target, method, args, methodProxy));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>AopAction就是一个实现环切的抽象类，Invocation实现具体的反射调用目标方法的代码，上AopAction代码：<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public abstract class AopAction &#123;</span><br><span class="line"></span><br><span class="line">    public abstract Object invoke(Invocation invocation);</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>Invocation代码:<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class Invocation &#123;</span><br><span class="line">    private Object target;</span><br><span class="line">    private Method method;</span><br><span class="line">    private Object[] args;</span><br><span class="line">    private MethodProxy methodProxy;</span><br><span class="line"></span><br><span class="line">    public Invocation(Object target, Method method, Object[] args, MethodProxy methodProxy) &#123;</span><br><span class="line">        this.target = target;</span><br><span class="line">        this.method = method;</span><br><span class="line">        this.args = args;</span><br><span class="line">        this.methodProxy = methodProxy;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public Object invoke() &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            return methodProxy.invoke(target, args);</span><br><span class="line">        &#125; catch (Throwable throwable) &#123;</span><br><span class="line">            throwable.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    //getter、setter</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>至此就贴完了最简单的aop实现方式。</p>
<h4 id="根据注解进行Aop强化的工具类"><a href="#根据注解进行Aop强化的工具类" class="headerlink" title="根据注解进行Aop强化的工具类"></a>根据注解进行Aop强化的工具类</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class AopLoader implements Loader &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public void doLoad(Set&lt;Class&lt;?&gt;&gt; classSet) &#123;</span><br><span class="line">        // AOP强化</span><br><span class="line">        createChainsByAopAction(classSet);</span><br><span class="line">        createChainsByAnnotation(classSet);</span><br><span class="line">        enhanceBeanContainer();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private void enhanceBeanContainer() &#123;</span><br><span class="line">        Map&lt;String, Object&gt; beanMap = BeanContainer.getBeanMap();</span><br><span class="line">        //对bean容器里的bean进行强化（生成代理对象）</span><br><span class="line">        for (Map.Entry&lt;String, Object&gt; entry : beanMap.entrySet()) &#123;</span><br><span class="line">            Object obj = entry.getValue();</span><br><span class="line">            // || !obj.getClass().isAnnotationPresent(LSBean.class)</span><br><span class="line">            Class&lt;?&gt; clazz = obj.getClass();</span><br><span class="line">            if (clazz.isAssignableFrom(AopAction.class)) &#123;</span><br><span class="line">                continue; //不强化Aop拦截类</span><br><span class="line">            &#125;</span><br><span class="line">            Object enhanceInstance = AopHelper.enhance(obj);</span><br><span class="line">            beanMap.put(entry.getKey(), enhanceInstance);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private void createChainsByAnnotation(Set&lt;Class&lt;?&gt;&gt; classSet) &#123;</span><br><span class="line">        //扫描LSAround注解的类</span><br><span class="line">        Set&lt;Class&lt;?&gt;&gt; classes = ClassUtil.getClassesByAnnotation(classSet, LSAround.class);</span><br><span class="line">        for (Class&lt;?&gt; clazz : classes) &#123;</span><br><span class="line">            if (!BeanContainer.containsKey(clazz.getName()))</span><br><span class="line">                continue;</span><br><span class="line">            LSAround lsAround = clazz.getAnnotation(LSAround.class);</span><br><span class="line">            for (Class&lt;?&gt; aopActionClass : lsAround.value())</span><br><span class="line">                AopHelper.addClassAopAction(clazz, aopActionClass);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private void createChainsByAopAction(Set&lt;Class&lt;?&gt;&gt; classSet) &#123;</span><br><span class="line">        //扫描所有继承AopAction的类，如果有LSAspect注解就添加到缓存</span><br><span class="line">        Set&lt;Class&lt;?&gt;&gt; aopActionClasses = ClassUtil.getClassesBySuper(classSet, AopAction.class);</span><br><span class="line">        for (Class&lt;?&gt; clazz : aopActionClasses) &#123;</span><br><span class="line">            if (!clazz.isAnnotationPresent(LSAspect.class) || !clazz.isAnnotationPresent(LSBean.class)) &#123;</span><br><span class="line">                continue;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            LSAspect aspect = clazz.getAnnotation(LSAspect.class);</span><br><span class="line">            String pkg = aspect.value();</span><br><span class="line">            String cls = aspect.cls();</span><br><span class="line"></span><br><span class="line">            if (StringKit.notBlank(cls)) &#123;</span><br><span class="line">                String clsName = pkg + &quot;.&quot; + cls;</span><br><span class="line">                try &#123;</span><br><span class="line">                    Class&lt;?&gt; targetClass = Class.forName(clsName);</span><br><span class="line">                    AopHelper.addClassAopAction(targetClass, clazz);</span><br><span class="line">                &#125; catch (ClassNotFoundException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                ClassUtil.getClassesByPkg(classSet, pkg).forEach(targetClass -&gt; &#123;</span><br><span class="line">                    AopHelper.addClassAopAction(targetClass, clazz);</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>以上就完成了在AopAction实现类上通过LSAspect注解指定需要被拦截的类，在被拦截的类和方法上通过LSAround注解指定拦截器类实现简单的AOP环切。其他代码有兴趣的可以上我的github看完整实现</p>

                <hr>
                

                <ul class="pager">
                    
                    <li class="previous">
                        <a href="/2018/07/17/手写山寨spring基础功能-MVC-1-路由注册和参数绑定/" data-toggle="tooltip" data-placement="top"
                           title="手写山寨spring基础功能 - MVC(1) - 路由注册和参数绑定">&larr; Previous Post</a>
                    </li>
                    
                    
                    <li class="next">
                        <a href="/2018/07/17/手写山寨spring基础功能-IOC/" data-toggle="tooltip" data-placement="top"
                           title="手写山寨spring基础功能 - IOC">Next Post &rarr;</a>
                    </li>
                    
                </ul>

                

                


                <!--加入新的评论系统-->
                
            </div>

            <div class="hidden-xs col-sm-3 toc-col">
                <div class="toc-wrap">
                    <ol class="toc"><li class="toc-item toc-level-4"><a class="toc-link" href="#简介"><span class="toc-text">简介</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#动态代理的实现"><span class="toc-text">动态代理的实现</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#AOP实现的关键"><span class="toc-text">AOP实现的关键</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#根据注解进行Aop强化的工具类"><span class="toc-text">根据注解进行Aop强化的工具类</span></a></li></ol>
                </div>
            </div>
        </div>

        <div class="row">
            <!-- Sidebar Container -->

            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                sidebar-container">

                <!-- Featured Tags -->
                
                <section>
                    <!-- no hr -->
                    <h5 class="text-center">
                        <a href="/tags/">FEATURED TAGS</a>
                    </h5>
                    <div class="tags">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#spring"
                           title="spring">spring</a>
                        
                    </div>
                </section>
                

                <!-- Friends Blog -->
                
            </div>
        </div>

    </div>
</article>







<!-- Footer -->
<!-- Footer -->
<footer>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <br>
                <ul class="list-inline text-center">
                
                
                
                    <li>
                        <a target="_blank" href="https://www.zhihu.com/people/asdf1234-97">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa  fa-stack-1x fa-inverse">知</i>
                            </span>
                        </a>
                    </li>
                

                

                

                
                    <li>
                        <a target="_blank"  href="https://github.com/xiuqianli1996">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-github fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
                

                

                </ul>
                <p class="copyright text-muted">
                    Copyright &copy; Xiuqianli&#39;s blog 2018
                    <br>
                    <span id="busuanzi_container_site_pv" style="font-size: 12px;">PV: <span id="busuanzi_value_site_pv"></span> Times</span>
                    <br>
                    Theme by <a href="https://haojen.github.io/">Haojen Ma</a>
                </p>

            </div>
        </div>
    </div>
</footer>

<!-- jQuery -->
<script src="/js/jquery.min.js"></script>

<!-- Bootstrap Core JavaScript -->
<script src="/js/bootstrap.min.js"></script>

<!-- Custom Theme JavaScript -->
<script src="/js/blog.js"></script>

<!-- async load function -->
<script>
    function async(u, c) {
      var d = document, t = 'script',
          o = d.createElement(t),
          s = d.getElementsByTagName(t)[0];
      o.src = u;
      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
      s.parentNode.insertBefore(o, s);
    }
</script>

<!-- jquery.tagcloud.js -->
<script>
    // only load tagcloud.js in tag.html
    if($('#tag_cloud').length !== 0){
        async("https://981764793.gitee.io/js/jquery.tagcloud.js",function(){
            $.fn.tagcloud.defaults = {
                //size: {start: 1, end: 1, unit: 'em'},
                color: {start: '#bbbbee', end: '#0085a1'},
            };
            $('#tag_cloud a').tagcloud();
        })
    }
</script>

<!--fastClick.js -->
<script>
    async("//cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function(){
        var $nav = document.querySelector("nav");
        if($nav) FastClick.attach($nav);
    })
</script>

<!-- Google Analytics -->



<!-- Baidu Tongji -->


<!-- swiftype -->
<script type="text/javascript">
  (function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
  (w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
  e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
  })(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');

  _st('install','null','2.0.0');
</script>

<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<!--wechat title img-->
<img class="wechat-title-img" src="null">
</body>

</html>
